///////////////////////////////////////////////////////////////////////////////////////////////////
/// \file command.h
///
/// \brief   header file for the class used to manage commands
///
///////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef PCPS_COMMAND_HEADER
#define PCPS_COMMAND_HEADER

#include <src/pcps.h>
#include <src/input.h>
#include <src/archive.h>

namespace pcps {

  // forward declaration of the AllData class
  class AllData;

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  Base class for commands used to control the PCPS program;
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  class Command {

    public:

      /// \brief  Execute the command.
      virtual void execute(pcps::Archive & arch, pcps::AllData & data) = 0;

      /// \brief  Destructor.  Declared virtual so that deleting pointers to derived members will
      ///         call the base class's destructor.
      virtual ~Command() {}

  };

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  A placeholder command that does nothing when executed.
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  class BlankCommand : public pcps::Command {

    public:

      /// \brief  Returns the keyword for this particular child of the Command class.
      static std::string keyword() { return "Blank"; }

      /// \brief  The execution of a BlankCommand does nothing.
      void execute(pcps::Archive & arch, pcps::AllData & data) {}

  };

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  A command used to ignore comments in text input files.
  ///
  ///  Comments are written in the following syntax.  Note that it is essential to have a space
  ///  character following the %% characters in order for the comment to be properly ignored.
  ///
  ///    %% This is a comment.
  ///    %% This is another comment.
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  class CommentCommand : public pcps::Command {

    public:

      /// \brief  Returns the keyword for this particular child of the Command class.
      static std::string keyword() { return "%%"; }

      void execute(pcps::Archive & arch, pcps::AllData & data);

  };

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  A command for setting user input options.
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  class OptionCommand : public pcps::Command {

    public:

      /// \brief  Returns the keyword for this particular child of the Command class.
      static std::string keyword() { return "Option"; }

      void execute(pcps::Archive & arch, pcps::AllData & data);

  };

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  A command for inputting wavefunction data.
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  template<class T> class WfnDataCommand : public pcps::Command {

    public:

      /// \brief  Returns the keyword for this particular child of the Command class.
      static std::string keyword() {
        if      (typeid(T) == typeid(int)                 ) return "WfnIntegerData";
        else if (typeid(T) == typeid(double)              ) return "WfnRealData";
        else if (typeid(T) == typeid(std::complex<double>)) return "WfnComplexData";
        else throw pcps::Exception("Unknown T type \"%s\" in pcps::WfnDataCommand::keyword") % typeid(T).name();
        return "";
      }

      void execute(pcps::Archive & arch, pcps::AllData & data);

  };

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  A command for inputting a correlator.
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  class OverallCoeffCommand : public pcps::Command {

    public:

      /// \brief  Returns the keyword for this particular child of the Command class.
      static std::string keyword() { return "OverallCoeff"; }

      void execute(pcps::Archive & arch, pcps::AllData & data);

  };

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  A command for inputting a correlator.
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  class CorrelatorCommand : public pcps::Command {

    public:

      /// \brief  Returns the keyword for this particular child of the Command class.
      static std::string keyword() { return "Correlator"; }

      void execute(pcps::Archive & arch, pcps::AllData & data);

  };

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  A command for inputting a correlator product.
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  class CorrProdCommand : public pcps::Command {

    public:

      /// \brief  Returns the keyword for this particular child of the Command class.
      static std::string keyword() { return "CorrProd"; }

      void execute(pcps::Archive & arch, pcps::AllData & data);

  };

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  A command for inputting a restricted pairing function.
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  class RestPairingFuncCommand : public pcps::Command {

    public:

      /// \brief  Returns the keyword for this particular child of the Command class.
      static std::string keyword() { return "RestPairingFunc"; }

      void execute(pcps::Archive & arch, pcps::AllData & data);

  };

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  A command for executing a linear method optimization
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  class LinearMethodCommand : public pcps::Command {

    public:

      /// \brief  Returns the keyword for this particular child of the Command class.
      static std::string keyword() { return "LinearMethod"; }

      void execute(pcps::Archive & arch, pcps::AllData & data);

  };

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  A command for loading integrals from psi3
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  class Psi3IntsCommand : public pcps::Command {

    public:

      /// \brief  Returns the keyword for this particular child of the Command class.
      static std::string keyword() { return "Psi3Ints"; }

      void execute(pcps::Archive & arch, pcps::AllData & data);

  };

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  A command for loading integrals from molpro
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  class MolproIntsCommand : public pcps::Command {

    public:

      /// \brief  Returns the keyword for this particular child of the Command class.
      static std::string keyword() { return "MolproInts"; }

      void execute(pcps::Archive & arch, pcps::AllData & data);

  };

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  A command for running a restricted hartree fock calculation
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  class RHFCommand : public pcps::Command {

    public:

      /// \brief  Returns the keyword for this particular child of the Command class.
      static std::string keyword() { return "RHF"; }

      void execute(pcps::Archive & arch, pcps::AllData & data);

  };

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  A command for running an unrestricted hartree fock calculation
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  class UHFCommand : public pcps::Command {

    public:

      /// \brief  Returns the keyword for this particular child of the Command class.
      static std::string keyword() { return "UHF"; }

      void execute(pcps::Archive & arch, pcps::AllData & data);

  };

  boost::shared_ptr<pcps::Command> get_command(const std::string & key);

  template<class T> void run_commands(const std::string & filename, pcps::AllData & data);

}

#endif
